﻿using Sidvall.Serialization;
using System.Collections.Generic;
using System.Linq;

namespace Sidvall.Data
{
    public class ParameterCollection : System.Collections.ObjectModel.KeyedCollection<string, Parameter>
    {
        #region Public Members

        #region Add

        public void Add(string key, object value, DataType dataType)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeObject(value, dataType),
            };
            Add(item);
        }
        public void Add(string key, object value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeObject(value),
            };
            Add(item);
        }
        public void Add(string key, bool? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, bool value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, byte? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, byte value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, char? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, char value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, System.DateTime? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, System.DateTime value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, System.DateTimeOffset? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, System.DateTimeOffset value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, decimal? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, decimal value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, double? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, double value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, short? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, short value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, int? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, int value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, long? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, long value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, float? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, float value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, string value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, System.TimeSpan? value)
        {
            if (value == null)
                return;
            Add(key, value.Value);
        }
        public void Add(string key, System.TimeSpan value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, byte[] value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value),
            };
            Add(item);
        }
        public void Add(string key, System.Enum value)
        {
            var parameterValue = GetParameterEnumValue(value);
            if (parameterValue == null)
                return;
            var item = new Parameter()
            {
                Key = key,
                Value = parameterValue,
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<decimal> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<double> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<byte> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<short> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<int> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<long> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<string> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }
        public void Add(string key, IEnumerable<System.Guid> value)
        {
            var item = new Parameter()
            {
                Key = key,
                Value = Sidvall.Serialization.SerializerManager.Current.SerializeArray(value),
            };
            Add(item);
        }

        #endregion
        #region AddEnumValues

        public void AddEnumValues<T>(string key, IEnumerable<T> value) where T : struct
        {
            if (value == null)
                return;

            var items = new List<long>();
            foreach (var valueItem in value)
            {
                var parameterValue = GetParameterEnumValue(valueItem).ToNullableInt64();
                if (parameterValue != null)
                    items.Add(parameterValue.Value);
            }
            Add(key, items);
        }

        #endregion
        #region AddRange

        public void AddRange(IEnumerable<Parameter> items)
        {
            if (items == null)
                return;
            foreach (var item in items)
                Add(item);
        }

        #endregion
        #region GetKeyForItem

        protected override string GetKeyForItem(Parameter item)
        {
            if (item == null)
                throw new System.ArgumentNullException("item");

            return item.Key;
        }

        #endregion
        #region Merge

        public void Merge(string key, bool? value)
        {
            Parameter p;

            if (Contains(key))
            {
                if (value != null)
                {
                    p = this[key];
                    p.Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value.Value);
                }
                else
                {
                    Remove(key);
                }
            }
            else
            {
                Add(key, value);
            }
        }
        public void Merge(string key, long? value)
        {
            Parameter p;

            if (Contains(key))
            {
                if (value != null)
                {
                    p = this[key];
                    p.Value = Sidvall.Serialization.SerializerManager.Current.Serialize(value.Value);
                }
                else
                {
                    Remove(key);
                }
            }
            else
            {
                Add(key, value);
            }
        }

        #endregion
        #region RemoveAllExcept

        public void RemoveAllExcept(IEnumerable<string> keys)
        {
            HashSet<string> index;
            Parameter item;
            int iMax;

            if ((keys != null) && (keys.Any()))
            {
                index = new HashSet<string>(keys, System.StringComparer.OrdinalIgnoreCase);
                iMax = Count;
                for (int i = iMax - 1; i >= 0; i--)
                {
                    item = this[i];
                    if (!index.Contains(item.Key))
                        RemoveAt(i);
                }
            }
            else
            {
                Clear();
                return;
            }
        }

        #endregion

        #region ValueToEnum

        public T ValueToEnum<T>(string key, T defaultValue) where T : struct
        {
            if (Contains(key))
                return this[key].Value.ToEnum<T>(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToBoolean

        public bool ValueToBoolean(string key, bool defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToBoolean(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToChar

        public char ValueToChar(string key, char defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToChar(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToDateTime

        public System.DateTime ValueToDateTime(string key, System.DateTime defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToDateTime(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToDateTimeOffset

        public System.DateTimeOffset ValueToDateTimeOffset(string key, System.DateTimeOffset defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToDateTimeOffset(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToDecimal

        public decimal ValueToDecimal(string key, decimal defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToDecimal(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToDouble

        public double ValueToDouble(string key, double defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToDouble(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToByte

        public byte ValueToByte(string key, byte defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToByte(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToInt16

        public short ValueToInt16(string key, short defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToInt16(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToInt32

        public int ValueToInt32(string key, int defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToInt32(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToInt64

        public long ValueToInt64(string key, long defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToInt64(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToSingle

        public float ValueToSingle(string key, float defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToSingle(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToString

        public string ValueToString(string key, string defaultValue)
        {
            if (Contains(key))
                return this[key].Value;
            return defaultValue;
        }

        #endregion
        #region ValueToTimeSpan

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "Sidvall.Serialization.StringExtension.ToTimeSpan(System.String,System.TimeSpan)")]
        public System.TimeSpan ValueToTimeSpan(string key, System.TimeSpan defaultValue)
        {
            if (Contains(key))
                return this[key].Value.ToTimeSpan(defaultValue);
            return defaultValue;
        }

        #endregion
        #region ValueToObject

        public T ValueToObject<T>(string key, T defaultValue)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.Deserialize<T>(this[key].Value);
            return defaultValue;
        }

        #endregion

        #region ValueToNullableEnum

        public T? ValueToNullableEnum<T>(string key) where T : struct
        {
            if (Contains(key))
                return this[key].Value.ToNullableEnum<T>();
            return null;
        }

        #endregion
        #region ValueToNullableBoolean

        public bool? ValueToNullableBoolean(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableBoolean();
            return null;
        }

        #endregion
        #region ValueToNullableChar

        public char? ValueToNullableChar(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableChar();
            return null;
        }

        #endregion
        #region ValueToNullableDateTime

        public System.DateTime? ValueToNullableDateTime(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableDateTime();
            return null;
        }

        #endregion
        #region ValueToNullableDateTimeOffset

        public System.DateTimeOffset? ValueToNullableDateTimeOffset(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableDateTimeOffset();
            return null;
        }

        #endregion
        #region ValueToNullableDecimal

        public decimal? ValueToNullableDecimal(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableDecimal();
            return null;
        }

        #endregion
        #region ValueToNullableDouble

        public double? ValueToNullableDouble(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableDouble();
            return null;
        }

        #endregion
        #region ValueToNullableByte

        public byte? ValueToNullableByte(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableByte();
            return null;
        }

        #endregion
        #region ValueToNullableInt16

        public short? ValueToNullableInt16(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableInt16();
            return null;
        }

        #endregion
        #region ValueToNullableInt32

        public int? ValueToNullableInt32(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableInt32();
            return null;
        }

        #endregion
        #region ValueToNullableInt64

        public long? ValueToNullableInt64(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableInt64();
            return null;
        }

        #endregion
        #region ValueToNullableSingle

        public float? ValueToNullableSingle(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableSingle();
            return null;
        }

        #endregion
        #region ValueToNullableTimeSpan

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "Sidvall.Serialization.StringExtension.ToNullableTimeSpan(System.String)")]
        public System.TimeSpan? ValueToNullableTimeSpan(string key)
        {
            if (Contains(key))
                return this[key].Value.ToNullableTimeSpan();
            return null;
        }

        #endregion

        #region ValueToEnumerableEnum

        public IEnumerable<T> ValueToEnumerableEnum<T>(string key) where T : struct
        {
            if (Contains(key))
            {
                var value = this[key].Value;
                var items = value.Split(new string[] { Sidvall.Serialization.SerializerManager.NumberSeparator }, System.StringSplitOptions.RemoveEmptyEntries);
                var enumArray = new System.Collections.Generic.List<T>();
                foreach (var item in items)
                {
                    var itemValue = item.ToNullableEnum<T>();
                    if (itemValue != null)
                        enumArray.Add(itemValue.Value);
                }
                return enumArray.ToArray();
            }
            return null;
        }

        #endregion
        #region ValueToEnumerableDecimal

        public IEnumerable<decimal> ValueToEnumerableDecimal(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.Decimal) as IEnumerable<decimal>;
            return null;
        }

        #endregion
        #region ValueToEnumerableDouble

        public IEnumerable<double> ValueToEnumerableDouble(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.Double) as IEnumerable<double>;
            return null;
        }

        #endregion
        #region ValueToEnumerableByte

        public IEnumerable<byte> ValueToEnumerableByte(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.Byte) as IEnumerable<byte>;
            return null;
        }

        #endregion
        #region ValueToEnumerableInt16

        public IEnumerable<short> ValueToEnumerableInt16(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.Int16) as IEnumerable<short>;
            return null;
        }

        #endregion
        #region ValueToEnumerableInt32

        public IEnumerable<int> ValueToEnumerableInt32(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.Int32) as IEnumerable<int>;
            return null;
        }

        #endregion
        #region ValueToEnumerableInt64

        public IEnumerable<long> ValueToEnumerableInt64(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.Int64) as IEnumerable<long>;
            return null;
        }

        #endregion
        #region ValueToEnumerableString

        public IEnumerable<string> ValueToEnumerableString(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.String) as IEnumerable<string>;
            return null;
        }

        #endregion
        #region ValueToEnumerableGuid

        public IEnumerable<System.Guid> ValueToEnumerableGuid(string key)
        {
            if (Contains(key))
                return Sidvall.Serialization.SerializerManager.Current.DeserializeArray(this[key].Value, DataType.Guid) as IEnumerable<System.Guid>;
            return null;
        }

        #endregion

        #endregion
        #region Private Members

        #region GetParameterEnumValue

        private static string GetParameterEnumValue(object value)
        {
            if (value == null)
                return null;
            var enumType = value.GetType();
            if (!System.Enum.IsDefined(enumType, value))
                return null;
            var valueType = System.Enum.GetUnderlyingType(enumType);
            var enumValue = System.Enum.Parse(enumType, value.ToString(), true);
            if (valueType == typeof(System.Byte))
            {
                return Sidvall.Serialization.SerializerManager.Current.Serialize((byte)enumValue);
            }
            else if (valueType == typeof(System.Int16))
            {
                return Sidvall.Serialization.SerializerManager.Current.Serialize((short)enumValue);
            }
            else if (valueType == typeof(System.Int32))
            {
                return Sidvall.Serialization.SerializerManager.Current.Serialize((int)enumValue);
            }
            else if (valueType == typeof(System.Int64))
            {
                return Sidvall.Serialization.SerializerManager.Current.Serialize((long)enumValue);
            }
            return null;
        }

        #endregion

        #endregion
        #region Constructors

        public ParameterCollection()
        {
        }

        #endregion
    }
}
